package ua.kpi_java_training.tyshchenko.module1;

import java.util.Scanner;

/**
 * Class get two numbers: dividend and divisor from the console.
 * The console output decision to divide these numbers in a column
 *
 * @version 1.4 25 May 2013
 * @author Vlad Tyshchenko
 */
public class Division {
    int dividend, divisor;
    int amountDigitDividend = 0, amountDigitDivisor = 0;
    int dividendInsideIteration = 0;
    int multiply = 0;
    int result = 0;
    String output = "";

    public static void main(String[] args) {
        Division div = new Division();
        div.inputDividendAndDivisor();
        div.getFirstResults();
        div.outputHeader();
        for (int loopCount = 0; loopCount < div.amountDigitDividend - div.amountDigitDivisor; loopCount++){
            div.getNextResults(loopCount);
            div.drawCentre(loopCount);
        }
        div.outputEnd();
        System.out.println(div.output);
    }

    /**
     * Method gets input data. If input data isn't number then the program stops.
     *
     * @return - input number
     */
    int inputNumber(String message) {
        int number;
        System.out.println(message);
        Scanner sc = new Scanner(System.in);

        while (!sc.hasNextInt()) {
            System.out.println("Error. " + message);
            sc.next();
        }
        number = sc.nextInt();
        return number;
    }

    /**
     * Method takes two numbers: dividend and divisor and checks the condition:
     * If |dividend| > |divisor| or divisor == 0 then data is requested again.
     */
    void inputDividendAndDivisor() {
        dividend = inputNumber("Enter dividend: ");
        divisor = inputNumber("Enter divisor: ");
        while (divisor == 0) {
            System.out.println("Divisor mustn't be equal null" );
            divisor = inputNumber("Enter divisor: ");
        }
        while (Math.abs(dividend) < Math.abs(divisor)) {
            System.out.println("Input error. Must be satisfied: |dividend| > |divisor|" );
            dividend = inputNumber("Enter dividend: ");
            divisor = inputNumber("Enter divisor: ");
        }
    }

    /**
     * Method find sign of the result.
     * @return - sign of the result
     */
    int getSign() {
        int signDividend = 1, signDivisor = 1, sign = 1;
        if (dividend < 0) {
            signDividend = -1;
        }
        if (divisor < 0) {
            signDivisor = -1;
        }
        sign = signDividend * signDivisor;
        return sign;
    }

    /**
     * Method draw line certain symbol.
     *
     * @param amount - amount of symbol in line.
     * @param symbol - symbol which draw line.
     * @return - character string
     */
    String draw(int amount, char symbol) {
        String line = "";
        String symbolString = "";
        if (symbol == ' ') {
            line = (amount == 0) ? "" : String.format("%" + amount + "s", "");
        } else {
            symbolString = Character.toString(symbol);
            line = String.format("%0" + amount + "d", 0).replace("0", symbolString);
        }
        return line;
    }

    /**
     * Method output top piece of column in line.
     */
    void outputHeader() {
        output += dividend;
        output += draw(8 - amountDigitDividend, ' ') + "| ";
        output += divisor + "\n";
        output += multiply;
        output += draw(8 - amountDigitDivisor, ' ') + "|";
        output += draw(7,'-') + "\n";
        output += draw(amountDigitDividend + 1, '-');
        output += draw(8 - amountDigitDividend, ' ');
        output += "| " + result + "\n";
    }

    /**
     * Method output the interval of center in line.
     *
     * @param loopCount - number of the interval.
     */
    void drawCentre(int loopCount) {
        output += draw(loopCount + 1, ' ');
        output += dividendInsideIteration + "\n";
        output += draw(loopCount+1,' ');
        output += multiply + "\n";
        output += draw(loopCount+1,' ');
        output += draw(amountDigitDividend, '-') + "\n";
        output += draw(loopCount, ' ');
    }

    /**
     * Method output end part of column.
     */
    void outputEnd() {
        output += draw(amountDigitDivisor ,' ');
        output += (dividendInsideIteration - multiply) + "\n";
        output += "\n result: " + result + "\n balance: " + (dividendInsideIteration - multiply);
    }

    /**
     * Method finds amount digit in number.
     * @param number - number which method processed.
     * @return - amount digit in number.
     */
    int getAmountDigit(int number) {
        int copyNumber = Math.abs(number);
        int amount = 0;
        final int CONST = 10; // this constant divides number that find out how much digits contains in number
        while (copyNumber >= 1) {
            copyNumber /= CONST;
            amount++;
        }
        return amount;
    }

    /**
     * Method finds increase that added to the difference of numbers (underDividend - multiply)
     *
     * @param loopCount - number of the interval the column
     * @return - increase.
     */
    int getIncrease(int loopCount) {
        double dividendInsideIterationDouble, dividendInsideIterationInt;
        double increase = 0;
        int increaseInteger = 0;
        final double ROUND = 0.49; //this constant helps round number to the left
        dividendInsideIterationInt = Math.abs(dividend)
                                     / Math.pow(10, (amountDigitDividend - amountDigitDivisor - loopCount));
        dividendInsideIterationInt = Math.round(dividendInsideIterationInt - ROUND);
        dividendInsideIterationDouble = Math.abs(dividend)
                                        / Math.pow(10, (amountDigitDividend - amountDigitDivisor - loopCount));
        increase = (dividendInsideIterationDouble - dividendInsideIterationInt) * 10;
        increase = Math.round(increase - ROUND);
        increaseInteger = (int) increase;
        return increaseInteger;
    }

    /**
     * Method check the correctness of dividendInsideIteration - the number is dividend of each interval the column.
     * If dividendInsideIteration is uncorrect then change dividendInsideIteration and amountDigitDividend.
     *
     * @param increase - added to underDividend if number is incorrect.
     */
    void checkDividendInsideIteration(double increase) {
        if (dividendInsideIteration < Math.abs(divisor)) {
            dividendInsideIteration = dividendInsideIteration * 10 + (int) increase;
            amountDigitDividend--;
        }
    }

    /**
     * Method find multiplication of result and underDividend;
     */
    void getFirstResults() {
        final int LOOP_COUNT = 0; // this constant use to get the first multiplication
        int increase = 0;                                    
        amountDigitDividend = getAmountDigit(dividend);
        amountDigitDivisor = getAmountDigit(divisor);
        dividendInsideIteration = Math.abs(dividend) / (int) Math.pow(10, (amountDigitDividend - amountDigitDivisor));
        increase = getIncrease(LOOP_COUNT);
        checkDividendInsideIteration(increase);
        multiply = (dividendInsideIteration / divisor) * divisor * getSign();
        result = dividend / divisor;
    }

    /**
     * Method finds the number is dividend of each interval the column.
     *
     * @param loopCount - number of interval the column.
     * @param increase  - add to the difference of numbers (underDividend - multiply).
     */
    int getNextDividendInsideIteration(int loopCount, double increase) {
        int nextDividend = 0;
        int subtract;
        int sign = getSign();
        subtract = Math.abs(dividendInsideIteration) - Math.abs(multiply);
        nextDividend = (subtract * 10 + (int) increase) * sign;
        return nextDividend;
    }

    /**
     * Method find multiplication of result and underDividend of each interval the column;
     * @param loopCount - number of interval the column.
     */
    void getNextResults(int loopCount) {
        double increase = 0;
        int sign = getSign();
        increase = getIncrease(loopCount);
        dividendInsideIteration = getNextDividendInsideIteration(loopCount, increase);
        multiply = (dividendInsideIteration / divisor) * divisor;
    }

}
